package com.yyfax.pay.quartz.service.impl;

import java.util.Map;

import javax.annotation.PostConstruct;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Service;

import com.google.common.collect.Maps;
import com.yyfax.commons.http.HttpUtil;
import com.yyfax.commons.json.JacksonUtil;
import com.yyfax.framework.common.Log;
import com.yyfax.framework.model.EmptyContent;
import com.yyfax.framework.spring.ApplicationContextHolder;
import com.yyfax.pay.common.annotation.NotifyType;
import com.yyfax.pay.common.constants.PayConstants;
import com.yyfax.pay.common.enums.NotifyTaskTypeEnum;
import com.yyfax.pay.common.exception.PayExceptionCode;
import com.yyfax.pay.common.model.to.YYPayRsp;
import com.yyfax.pay.order.model.po.NotifyTaskPO;
import com.yyfax.pay.quartz.adapter.NotifyHandlerAdapter;
import com.yyfax.pay.quartz.model.vo.TaskResultVO;
import com.yyfax.pay.quartz.service.NotifyHandlerService;
import com.yyfax.pay.third.model.to.yyfax.NotifyDataBinder;

@Service
public class NotifyHandlerServiceImpl implements NotifyHandlerService {
	private Logger logger = LoggerFactory.getLogger(getClass());

	private final Map<NotifyTaskTypeEnum, NotifyHandlerAdapter> notifyHandlerMap = Maps.newHashMap();

	@PostConstruct
	public void init() {
		Map<String, NotifyHandlerAdapter> adpaterMap = ApplicationContextHolder.getApplicationContext().getBeansOfType(NotifyHandlerAdapter.class);
		for (Object key : adpaterMap.keySet()) {
			NotifyHandlerAdapter adpter = adpaterMap.get(key);
			NotifyType anno = AnnotationUtils.findAnnotation(adpter.getClass(), NotifyType.class);
			if (null != anno) {
				notifyHandlerMap.put(anno.value(), adpter);
			}
		}
	}

	private NotifyDataBinder doHttp(NotifyTaskPO task) {
		String op = "NotifyPayOrderJob.doPost";

		NotifyDataBinder binder = new NotifyDataBinder();
		binder.setTask(task);
		binder.setBusiId(task.getTradeOrderId());
		try {

			binder.setReqUrl(task.getNotifyUrl());
			binder.setReqContent(task.getReqContent());

			// 发送请求
			String rspJson = HttpUtil.postBodyDirect(task.getNotifyUrl(), task.getReqContent(), null, null, null, null, null,
					PayConstants.DEFAULT_CONNECT_TIMEOUT, PayConstants.DEFAULT_READ_TIMEOUT);

			binder.setRspContent(rspJson);

			logger.info(Log.op(op).msg("异步通知业务方，响应").kv("NotifyTaskPO", task).kv("响应报文", rspJson).toString());

			if (StringUtils.isBlank(rspJson)) {
				binder.fail();
			}

			YYPayRsp<EmptyContent> rsp = JacksonUtil.fromJson(rspJson, YYPayRsp.class, EmptyContent.class);
			if (rsp == null) {
				binder.fail();
			}

			if (PayExceptionCode.SUCCESS.isCode(rsp.getCode())) {
				// 只有响应成功状态，才返回成功
				binder.success();
			}
		} catch (Exception e) {
			logger.error(Log.op(op).msg("异步通知业务方，请求异常").kv("NotifyTaskPO", task).toString());
			binder.setException(e);
			binder.fail();
		}

		return binder;
	}

	/**
	 * 发送通知
	 * 
	 * @author buyi
	 * @date 2018-05-25 17:07:55
	 * @since v1.0.0
	 * @param task
	 * @return
	 */
	@Override
	public TaskResultVO doPost(NotifyTaskPO task) {
		NotifyDataBinder binder = doHttp(task);
		if (binder.isSendSuccess() && !binder.isError()) {
			// 如果发送状态是成功，且无异常，则表示任务可以关闭
			return new TaskResultVO(true);
		}

		doHandler(binder);

		return new TaskResultVO(false);
	}

	/**
	 * 执行服务
	 * 
	 * @author buyi
	 * @date 2018-05-25 20:08:29
	 * @since v1.0.0
	 * @param binder
	 */
	private void doHandler(NotifyDataBinder binder) {
		try {
			NotifyHandlerAdapter notifyHandlerAdapter = getNotifyHandlerAdapter(binder.getTask().getType());
			if (notifyHandlerAdapter == null) {
				logger.info(Log.op("").msg("异步通知没有实现业务逻辑，不执行").kv("busiId", binder.getBusiId()).toString());
				;
				return;
			}

			notifyHandlerAdapter.handle(binder);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 获取查询异步通知子业务服务
	 * 
	 * @author buyi
	 * @date 2018-05-25 20:08:11
	 * @since v1.0.0
	 * @param type
	 * @return
	 */
	private NotifyHandlerAdapter getNotifyHandlerAdapter(String type) {
		NotifyTaskTypeEnum typeEnum = NotifyTaskTypeEnum.fromValue(type);
		if (typeEnum == null) {
			return null;
		}

		NotifyHandlerAdapter handlerAdapter = notifyHandlerMap.get(typeEnum);
		return handlerAdapter;
	}
}
